﻿/*

BatchAjaxSaveModule uses the configured liteGrid data provider to persist all changes in the grid
back to the provider.  It can only be used with a data provider that supports saving, and it
requires the presence of the ToolbarModule. 

Author: MBH
DateAuthored: 7/17/09  
*/
function BatchSaveModule() {
	var base = this;

	//Register for events we care about and store needed references.
	base.initialize = function(liteGrid, options) {

		base.liteGrid = liteGrid;
		base.dataProvider = liteGrid.dataProvider;
		//Initialize module-specific options
		base.options = $.extend({}, BatchSaveModule.prototype.defaultOptions, options);

		//Make sure the data provider supports saving rows.
		if (!base.dataProvider.saveData) {
			console.error("Data provider does not support saving rows.  Be sure to use a compatibile data provider.");
			return;
		}

		liteGrid.$el.bind("initializationComplete", function() {
			//Make sure the toolbar module exists.  If not, this module can't be used. 
			if (!base.liteGrid.addToolbarButton) {
				console.error("BatchAjaxSaveModule requires ToolbarModule.  Be sure the module is initialized correctly.");
				return;
			}

			base.createButton();
			window.onbeforeunload = base.beforeUnload;
		});
	}

	//Callback that is run once all the modules have initialized.  At this point,
	//the grid should be in edit mode.
	base.createButton = function() {

		//Add a save method directly to the grid so that other modules can use it.
		base.liteGrid.batchSaveChanges = base.batchSaveChanges;

		//Add a button to the toolbar for the method.  The action is delayed by a few MS to insure
		//that the edit module has time to persist changes if the user goes straight from editing 
		//a field back to saving.
		base.liteGrid.addToolbarButton({ icon: "ui-icon-disk", text: base.options.batchSaveButtonLabel,
			action: function() {
				//A small delay is added so that editor modules have time to handle blur events
				//and persist any new values.  There might be a better way to handle this...
				setTimeout(base.batchSaveChanges, 200);
			}
		});
	}

	//Gets an object with a pair of arrays containing pending changes.  The first array is named "tableRows",
	//the second is "dataItems".  dataItems[i] corresponds to tableRows[i].
	base.getChanges = function() {
		var changes = { tableRows: [], dataItems: [] };

		$("tr:has(td.modified),tr.deleted", base.liteGrid.$el).each(function() {
			var row = this;
			changes.tableRows.push(row);
			changes.dataItems.push($(row).data("dataItem"));
		});

		return changes;
	}

	//This is the function that actually acquires changed rows and submits them to the server.
	//Callback is invoked after saving has finished.
	base.batchSaveChanges = function(callback) {

		//Modified rows
		var changes = base.getChanges();
		
		var beforeSaveEvent = $.Event("beforeSave");
		beforeSaveEvent.changes = changes;
		base.liteGrid.$el.trigger(beforeSaveEvent);
		// check for any cancellations...
		if (beforeSaveEvent.cancel) {
			var event = $.Event("actionCancelled");
			event.message = "Save Failed: " + beforeSaveEvent.cancelMessage;
			base.liteGrid.$el.trigger(event);
			return;
		}
		
		var dataItems = changes.dataItems;

		if (!$.isFunction(callback)) {
			callback = function() { };
		}

		//Can't save if there is nothing to save...
		if (dataItems.length == 0) {
			callback();
			return;
		}

		base.dataProvider.saveData(dataItems,
			//Success handler.
			function(data) {

				//Make sure the request succeeded.
				if (!data.status || data.status !== true) {
					base.raiseError(null, data.status, data.message);
				}
				else {

					//Process all changes.  There should be one for each modified row.
					$(changes.tableRows).each(function(i) {
						base.applyUpdates($(this), data.dataItems[i]);
					});

					//Remove modified flags
					$(changes.tableRows).children("td.modified").removeClass("modified ui-state-highlight");
				}

				base.liteGrid.$el.trigger("dataSaved", [data.dataItems, changes.tableRows]);

				callback();
			},
			//Error handler.
			function(request, status, error) {
				base.raiseError(request, status, error);
				callback();
			}
		);
	}

	//Raises an error event on the grid.
	base.raiseError = function(request, status, error) {
		var event = $.Event("BatchAjaxSaveError");
		event.request = request;
		event.status = status;
		event.error = error;
		base.liteGrid.raiseErrorEvent(event);
	}

	//Fired when the user tries to leave the page.  If there are changed rows, the user is prompted not
	//to leave.
	base.beforeUnload = function() {
		if (base.getChanges().dataItems.length > 0) {
			return "You have unsaved changes that will be discarded.";
		}
	}
	
	//Applies updates from a data item to its corresponding row.
	base.applyUpdates = function(row, dataItem) {

		//Remove deleted rows from the hierarchy. 
		if (dataItem[base.options.deleteColumnName] === true) {
			var rowsToDelete = row.data("rowsToDelete");
			rowsToDelete.remove();
		}
		//If the row wasn't deleted, re-bind it.
		else {
			base.liteGrid.bindRow($(row), dataItem);
		}
	}
}
BatchSaveModule.prototype.defaultOptions = {
	batchSaveButtonLabel: "Save"
};